//
// Created by javier on 2/11/15.
//

#ifndef GNSS_SIM_GPS_L1_CA_SIM_CHANNEL_CC_H
#define GNSS_SIM_GPS_L1_CA_SIM_CHANNEL_CC_H

#include "GPS_L1_CA.h"
#include "datetime.h"
#include "gps_ephemeris.h"
#include "gpstime.h"
#include "pseudorange.h"
#include <volk/volk_complex.h>
#include <cstdint>
#include <memory>
#include <vector>

class gps_l1_ca_sim_channel_cc
{
public:
    //EPHEMERIS DATA
    Gps_Ephemeris d_eph;
    gps_l1_ca_sim_channel_cc(Gps_Ephemeris eph, double sampling_freq);
    ~gps_l1_ca_sim_channel_cc();
    void codegen();
    void generate_telemetry(gpstime g0);
    void init_carrier_phase_and_range(gpstime grx, std::vector<double> obs_xyz);
    void set_output_buffer(std::complex<float> *out_buffer, int size);
    void update_observables(std::vector<double> obs_xyz, gpstime grx, gpstime g0);
    void update_AOA(std::vector<double> obs_xyz, gpstime grx);
    double get_pseudorange();
    double get_true_range();
    double get_doppler();
    double get_code_phase();
    double get_current_timestamp();
    double get_acc_carrier_phase();
    double get_acc_carrier_phase_l2();
    void generate_signal();
    void set_cn0(bool enable_noise, double signal_amplitude_lin);

    double d_el_deg{};
    double d_az_deg{};
    //current Time of Week
    double d_current_TOW{};

    std::complex<float> *d_output_buffer{};

private:
    /*! \brief Compute Satellite position, velocity and clock at given time
 *  \param[in] eph Ephemeris data of the satellite
 *  \param[in] g GPS time at which position is to be computed
 *  \param[out] pos Computed position (vector)
 *  \param[out] vel Computed velociy (vector)
 *  \param[clk] clk Computed clock
 */
    void satpos(Gps_Ephemeris *eph, gpstime g, double *pos, double *vel, double *clk);

    /*! \brief Subtract two vectors of double
 *  \param[out] y Result of subtraction
 *  \param[in] x1 Minuend of subtracion
 *  \param[in] x2 Subtrahend of subtracion
 */
    void subVect(double *y, const double *x1, const double *x2);

    /*! \brief Compute Norm of Vector
 *  \param[in] x Input vector
 *  \returns Length (Norm) of the input vector
 */
    double normVect(const double *x);

    /*! \brief Compute dot-product of two vectors
 *  \param[in] x1 First multiplicand
 *  \param[in] x2 Second multiplicand
 *  \returns Dot-product of both multiplicands
 */
    double dotProd(const double *x1, const double *x2);

    /*! \brief Compute range between a satellite and the receiver
 *  \param[out] rho The computed range
 *  \param[in] eph Ephemeris data of the satellite
 *  \param[in] g GPS time at time of receiving the signal
 *  \param[in] xyz position of the receiver
 */
    void computeRange(pseudorange *rho, Gps_Ephemeris *eph, gpstime g, double xyz[]);


    void eph2sbf();


    unsigned long countBits(unsigned long v);


    unsigned long computeChecksum(unsigned long source, int nib);

    /*! \brief Compute the code phase for a given channel (satellite)
 *  \param chan Channel on which we operate (is updated)
 *  \param[in] rho0 Range at start of interval
 *  \param[in] rho1 Current range, after \a dt has expired
 *  \param[in dt delta-t (time difference) in seconds
 *  \return current TOW
 */

    double computeCodePhase(pseudorange rho1, gpstime g0, double dt);


    // SPREADING CODE DATA
    int8_t *d_code_table;
    int8_t d_current_chip{};


    //TELEMETRY DATA
    int8_t d_dataBit{};
    int d_icode{};
    int d_ibit{};
    int d_iword{};
    unsigned long *d_tlm_words;
    unsigned long d_sbf[5][10]{};  //5 subframes, each with 10 words

    //CODE AND CARRIER STATUS
    double d_code_phase{};
    double d_carrier_phase{};
    double d_code_freq{};
    double d_carrier_freq{};
    double d_carrier_freq_l2{};
    double d_acc_carrier_phase_cycles;
    double d_acc_carrier_phase_l2_cycles;

    // PSEUDORAGE DATA
    pseudorange d_rho0;

    //SAMPLING PARAMETERS
    double d_sampling_time;
    double d_sampling_freq;
    double d_current_timestamp_s;
    bool d_enable_noise;
    double d_signal_amplitude_lin;

    // output buffer
    int d_buffer_size{};
};


#endif  //GNSS_SIM_GPS_L1_CA_SIM_CHANNEL_CC_H
